﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System.IO;
using BruteForceSerializer.Contract.Internal;
using Newtonsoft.Json;
using BruteForceSerializer.Normalization;
using System;

namespace BruteForceSerializer.Formatting
{
	internal static class Formatter
	{
		public static string Format(INormalizedObject objectGraphRoot, bool indent)
		{
			using (var sw = new StringWriter())
			{
				var jtw = new JsonTextWriter(sw);
				if (indent) jtw.Formatting = Newtonsoft.Json.Formatting.Indented;

				Format(jtw, objectGraphRoot);

				return sw.ToString();
			}
		}

		private static void Format(JsonTextWriter jtw, INormalizedObject objectGraphRoot)
		{
			if (objectGraphRoot == null)
			{
				jtw.WriteNull();
				return;
			}

			jtw.WriteStartObject();
			WriteJsonObject(jtw, objectGraphRoot);
			jtw.WriteEndObject();
		}


		private static void WriteJsonObject(JsonTextWriter jtw, INormalizedObject objectGraphRoot)
		{
			WriteObjectHeader(jtw, objectGraphRoot);
			WriteObjectFields(jtw, objectGraphRoot);
		}


		private static void WriteObjectHeader(JsonTextWriter jtw, INormalizedObject objectGraphRoot)
		{
			jtw.WritePropertyName("_Id");
			jtw.WriteValue(objectGraphRoot.Id);

			jtw.WritePropertyName("_Type");
			jtw.WriteValue(objectGraphRoot.Typename);
		}

		private static void WriteObjectFields(JsonTextWriter jtw, INormalizedObject objectGraphRoot)
		{
			foreach (var field in objectGraphRoot.Fields)
			{
				jtw.WritePropertyName(field.Name);
				if (field.Value is INormalizedObject)
					Format(jtw, (INormalizedObject)field.Value);
				else if (field.Value is INormalizedReference)
					WriteObjectReference(jtw, field);
				else
					jtw.WriteValue(field.Value);
			}
		}

		private static void WriteObjectReference(JsonTextWriter jtw, INormalizedField field)
		{
			jtw.WriteStartObject();

			jtw.WritePropertyName("_ObjectId");
			jtw.WriteValue(((INormalizedReference)field.Value).ObjectId);

			jtw.WriteEndObject();
		}

		public static INormalizedObject DeFormat(string objectString)
		{
			using (var sr = new StringReader(objectString))
			{
				var jtr = new JsonTextReader(sr);
				jtr.Read();
				return (INormalizedObject)DeFormat(jtr);
			}
		}

		private static object DeFormat(JsonTextReader jtr)
		{
			if (jtr.TokenType != JsonToken.StartObject)
				throw new Exception("TokenType should be StartObject");
			jtr.Read();
			if (jtr.Value.ToString() == "_Id")
				return DeFormatObject(jtr);
			if (jtr.Value.ToString() == "_ObjectId")
				return DeFormatObjectReference(jtr);
			return null;
		}

		private static NormalizedObject DeFormatObject(JsonTextReader jtr)
		{
			jtr.Read();
			int id = Convert.ToInt32(jtr.Value);
			jtr.Read();
			jtr.Read();
			string typename = jtr.Value.ToString();

			var no = new NormalizedObject(typename, id);
			//Console.WriteLine("ID: " + id + ", Type: " + typename);

			while (jtr.Read() && jtr.TokenType != JsonToken.EndObject)
			{
				string fieldname = jtr.Value.ToString();
				jtr.Read();
				if (jtr.TokenType == JsonToken.StartObject)
				{
					//Console.WriteLine("Field: " + fieldname);
					no.AddField(fieldname, DeFormat(jtr));
				}
				else
				{
					//Console.WriteLine("Field: " + fieldname + " = " + jtr.Value);
					no.AddField(fieldname, jtr.Value);
				}
			}
			return no;
		}

		private static NormalizedReference DeFormatObjectReference(JsonTextReader jtr)
		{
			jtr.Read();
			var normRef = new NormalizedReference(Convert.ToInt32(jtr.Value));
			jtr.Read();
			return normRef;
		}
	}
}
